package com.jwkj.collector.master;

import com.ghgande.j2mod.modbus.Modbus;
import com.ghgande.j2mod.modbus.io.ModbusRTUTCPTransport;
import com.ghgande.j2mod.modbus.io.ModbusRTUTCPTransportNew;
import com.ghgande.j2mod.modbus.io.ModbusTCPTransaction;
import com.ghgande.j2mod.modbus.io.ModbusTCPTransactionNew;
import com.ghgande.j2mod.modbus.io.ModbusTCPTransport;
import com.ghgande.j2mod.modbus.io.ModbusTCPTransportNew;
import com.ghgande.j2mod.modbus.msg.ModbusRequest;
import com.ghgande.j2mod.modbus.msg.ModbusResponse;
import com.ghgande.j2mod.modbus.msg.ReadCoilsRequest;
import com.ghgande.j2mod.modbus.msg.ReadInputDiscretesRequest;
import com.ghgande.j2mod.modbus.msg.ReadInputRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.ReadMultipleRegistersResponse;
import com.ghgande.j2mod.modbus.msg.WriteCoilRequest;
import com.ghgande.j2mod.modbus.msg.WriteMultipleCoilsRequest;
import com.ghgande.j2mod.modbus.msg.WriteMultipleRegistersRequest;
import com.ghgande.j2mod.modbus.msg.WriteSingleRegisterRequest;
import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
import com.ghgande.j2mod.modbus.util.BitVector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.Socket;
import java.util.Arrays;
import java.util.concurrent.Callable;

/**
 * Created by kaiping.wang on 2018/11/6.
 */
public class TcpRTUMaster{
    private static final Logger logger = LoggerFactory.getLogger(TcpRTUMaster.class);
    private Socket socket;

    public TcpRTUMaster(Socket socket) {
        this.socket = socket;
    }

    public synchronized ModbusResponse readRequest(int unitId, int functionCode, int register, int count) {
        ModbusTCPTransportNew transport = null;
        ModbusTCPTransactionNew trans;
        try {
            transport = new ModbusRTUTCPTransportNew(socket);
            ModbusRequest req = null;

            // Prepare the request
            switch (functionCode) {
                case Modbus.READ_COILS:
                    req = new ReadCoilsRequest(register, count);
                    break;
                case Modbus.READ_INPUT_DISCRETES:
                    req = new ReadInputDiscretesRequest(register, count);
                    break;
                case Modbus.READ_INPUT_REGISTERS:
                    req = new ReadInputRegistersRequest(register, count);
                    break;
                case Modbus.READ_HOLDING_REGISTERS:
                    req = new ReadMultipleRegistersRequest(register, count);
                    break;
                default:
                    throw new Exception(String.format("Request type %d is not supported by the test harness",
                                                      functionCode));
            }
            req.setUnitID(unitId);
            trans = (ModbusTCPTransactionNew) transport.createTransaction();
            trans.setRequest(req);
            trans.setReconnecting(false);
            trans.execute();
            return trans.getResponse();
        } catch (Exception e) {
            logger.info("Got error for request to {}:{} (FC:{}, Reg:{}, Cnt:{}) - {}",
                        functionCode,
                        register,
                        count,
                        e.getMessage());
        }
        return null;
    }


    public synchronized ModbusResponse writeRequest(int unitId, int functionCode, int register, int... values) {
        ModbusTCPTransportNew transport = null;
        ModbusTCPTransactionNew trans;
        try {
            transport = new ModbusRTUTCPTransportNew(socket);
            ModbusRequest req = null;

            // Prepare the request
            switch (functionCode) {
                case Modbus.WRITE_COIL:
                    req = new WriteCoilRequest(register, values[0] != 0);
                    break;
                case Modbus.WRITE_SINGLE_REGISTER:
                    req = new WriteSingleRegisterRequest(register, new SimpleRegister(values[0]));
                    break;
                case Modbus.WRITE_MULTIPLE_REGISTERS:
                    Register[] regs = new Register[values.length];
                    for (int i = 0; i < values.length; i++) {
                        regs[i] = new SimpleRegister(values[i]);
                    }
                    req = new WriteMultipleRegistersRequest(register, regs);
                    break;
                case Modbus.WRITE_MULTIPLE_COILS:
                    BitVector bitVector = new BitVector(values.length);
                    for (int i = 0; i < values.length; i++) {
                        bitVector.setBit(i, values[i] != 0);
                    }
                    req = new WriteMultipleCoilsRequest(register, bitVector);
                    break;
                default:
                    throw new Exception(String.format("Request type %d is not supported by the test harness",
                                                      functionCode));
            }
            req.setUnitID(unitId);
            trans = (ModbusTCPTransactionNew) transport.createTransaction();
            trans.setRequest(req);
            trans.setReconnecting(false);
            trans.execute();
            return trans.getResponse();
        } catch (Exception e) {
            logger.debug(e.getMessage());
        }
        return null;
    }

}
